/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date:    May. 2010
 *
 */

#ifndef __CPU_EDGE_OPN_HH__
#define __CPU_EDGE_OPN_HH__

#include <list>

#include "base/timebuf.hh"
#include "config/the_isa.hh"
#include "cpu/edge/opn/opn_fifo.hh"
#include "cpu/edge/opn/opn_mux.hh"
#include "cpu/edge/opn/opn_pkt.hh"

class DistributedEdgeCPUParams;

template<class Impl>
class OperandNetwork
{
  public:    
    typedef typename Impl::CPU CPU;
    typedef typename Impl::CPUPol CPUPol; 
    typedef typename Impl::DynInstPtr DynInstPtr;
    
    typedef typename CPUPol::ExeUnit ExeUnit;
    typedef typename CPUPol::OpnPktPtr OpnPktPtr;
    typedef typename CPUPol::OpnPkt OpnPkt;
    typedef typename CPUPol::OpnTimeStruct OpnTimeStruct;
    typedef typename TimeBuffer<OpnTimeStruct>::wire Wire;
    typedef ExeUnit Neighbor;

    typedef TheISA::Dir_t Dir_t;
    typedef TheISA::FlitType_t FlitType_t;

    /** Constructor. */
    OperandNetwork(CPU *cpu_ptr, ExeUnit *exe_unit,
                   DistributedEdgeCPUParams *params);

    /** Destructor. */
    ~OperandNetwork();

    /** Returns the name of the OPN. */
    std::string name() const;

  private:
    /** Hop latency of the operand network. */
    int hopLatency;

    /** Send occupancy of the operand network. */
    int sendOccupancy;

    /** Send latency of the operand network. */
    int sendLatency;

    /** Recieve occupancy of the operand network. */
    int receiveOccupancy;

    /** Receive latency of the operand network. */
    int receiveLatency;    

    /** Size of local input fifo. */
    int localInputFifoSize;

    /** Size of router fifo. */
    int routerFifoSize;

    /** Width of network. */
    int width;
    
    /** Pointer to timebuffer of each direction. */
    TimeBuffer<OpnTimeStruct> * timeBufferNout;
    TimeBuffer<OpnTimeStruct> * timeBufferNin;
    TimeBuffer<OpnTimeStruct> * timeBufferSout;
    TimeBuffer<OpnTimeStruct> * timeBufferSin;
    TimeBuffer<OpnTimeStruct> * timeBufferEout;
    TimeBuffer<OpnTimeStruct> * timeBufferEin;
    TimeBuffer<OpnTimeStruct> * timeBufferWout;
    TimeBuffer<OpnTimeStruct> * timeBufferWin;
    TimeBuffer<OpnTimeStruct> * timeBufferLout;
    TimeBuffer<OpnTimeStruct> * timeBufferLin;
    
  public:
    /** Topology of the whole network. */
    int x,y;
    
    /** Id of this opn. */
    int id;

    /** Pointer to cpu. */
    CPU *cpu;

    /** Pointer to exeUnit. */
    ExeUnit *exeUnit;

    /** Pointer to its neighbors. */ 
    Neighbor * neighN,* neighS,* neighE,* neighW;

    /** Fifo of each direction. */
    OpnFifo<Impl> *opnFF[5];

    /** Mux of each direction. */
    OpnMux  *opnMX[5];

    /**
     * Wire of each direction. Communication is done
     * through these wires.
     * */
    Wire opnNout, opnSout, opnEout, opnWout, opnLout;
    Wire opnNin, opnSin, opnEin, opnWin, opnLin;

    /** Which direction get available data from where. */
    bool muxValid[5][5];

    /** Destination of the head of a fifo. */
    Dir_t ffDestDirection[5];

    /** Direction of the mux given by the data in fifo. */
    Dir_t muxSelectDirection[5];

    /** Initialize this router. */
    void init (int x, int y, int id);

    /**
     * Decode a packet in head of the fifo to determine
     * the destnation 
     * of this packet.
     * */
    Dir_t decodePacketInFifo (OpnFifo<Impl> *f);

    /** Decode the source direction of a received packet. */
    Dir_t decodePacketReceived(OpnPktPtr &pkt);
    
    /** Squash this router. */
    void squash(TheISA::BlockID block_id);

    /** Push the input data from wire into fifo. */
    void pushToFifo();

    /** Update the credits. */
    void updateMuxCredit();

    /** Select a specific fifo. */
    void selectFifo();

    /** Select fifo of one specific direction. */
    Dir_t selectFifo(Dir_t src_dir);
    
    /** Clear the output wires. */
    void clearOutWires();

    /** Set the output wires. */
    int setOutWires(); 

    /** Squash the wires. */
    void squashWires(TheISA::BlockID block_id, int *flush_cnt);

    /** Tick function of this opn. */
    int promote();

    /** Send a packet. */
    void localInject(OpnPktPtr &pkt);

    /** Receive a packet from its neighbors. */
    void receive(OpnPktPtr &pkt);

    /** Set neighbor of a specific direction. */
    void setNeighbor(Neighbor * neigh, Dir_t direction);
    
    /** Set timebuffer for each direction. */
    void setTimeBuffer(TimeBuffer<OpnTimeStruct> *buff_ptr,
        Dir_t direction,bool in);

    /** Send a packet to this router. */
    void send(ExeUnit *dest_node,OpnPktPtr &pkt_to_send);

    /** Print the available packets right this moment. */
    void printIncoming();

    /** No valid packet in any fifo for the corresponding mux? */
    inline bool noFfValid (bool *valid_in_fifo) {
        if ( (valid_in_fifo[TheISA::OPN_N] == false) &&
             (valid_in_fifo[TheISA::OPN_S] == false) &&
             (valid_in_fifo[TheISA::OPN_E] == false) &&
             (valid_in_fifo[TheISA::OPN_W] == false) &&
             (valid_in_fifo[TheISA::OPN_L] == false) ) { 
           return true;
        } else {
           return false;
        }
    }
};

#endif //__CPU_EDGE_OPN_HH__

